package com.zyl.blog.common.utils.Extreme;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.zyl.blog.common.exception.BlogException;
import com.zyl.blog.common.response.Result;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @version v1.0
 * @ProjectName: Personal_Blog
 * @Package: com.zyl.common.utils.Extreme
 * @ClassName: CaptchaUtils
 * @Description: 验证码工具类
 * @Author FinalFantasy
 * @Date 2025/3/7-14:32
 */
@Component
@Data
public class CaptchaUtils {

    private static RedisTemplate<String, String> redisTemplate;

    /* 通过setter注入保证单例 */
    @Autowired
    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        CaptchaUtils.redisTemplate = redisTemplate;
    }

    /**
     * 存储验证码，并设置过期时间
     *
     * @param width  宽度
     * @param height 高度
     * @param expire 过期时间
     * @return {@link ResponseEntity }<{@link Map }<{@link String }, {@link String }>>
     */
    public static Result<?> generateCaptcha(Integer width, Integer height, Long expire) {
        /* 创建线段干扰验证码 */
        /* 可选类型：CircleCaptcha/LineCaptcha/ShearCaptcha */
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(width, height);
        String key = IdUtil.simpleUUID();

        /* 生成验证码内容并转化为Base64 */
        storeWithRetry(key, captcha.getCode(), expire);

        return Result.ok(Map.of(
                "key", key,
                "image", captcha.getImageBase64Data()
        ));
    }

    /**
     * 校验验证码（自动删除已使用验证码）
     * @param key Redis存储键
     * @param userInput 用户输入
     * @return 是否验证通过
     * @throws BlogException 验证码相关异常
     */
    public static boolean validateAndDelete(String key, String userInput) throws BlogException {
        try {
            Assert.notBlank(key, "验证码Key不能为空");
            Assert.notBlank(userInput, "验证码输入不能为空");

            String storedCode = redisTemplate.opsForValue().get(key);
            if (storedCode == null) {
                LogUtil.warn("[验证码校验] 验证码已过期或不存在，key: {}", key);
                throw new BlogException("验证码已失效，请刷新重试");
            }

            boolean isValid = storedCode.equalsIgnoreCase(userInput);
            if (isValid) {
                /* 验证成功后立即删除 */
                redisTemplate.delete(key);
                LogUtil.debug("[验证码校验] 验证成功，key: {}", key);
            } else {
                LogUtil.warn("[验证码校验] 验证码不匹配，key: {}，输入: {}", key, userInput);
            }
            return isValid;
        } catch (Exception e) {
            LogUtil.error("[验证码校验] 系统异常，key: {}", e, key);
            throw new BlogException("验证码服务异常", e);
        }
    }

    /**
     * 存储验证码（带重试机制）
     * @param key Redis键
     * @param code 验证码
     * @param expire 过期时间（分钟）
     */
    public static void storeWithRetry(String key, String code, long expire) {
        int maxRetries = 3;
        for (int i = 0; i < maxRetries; i++) {
            try {
                /* 将验证码内容存入Redis */
                redisTemplate.opsForValue().set(key, code, expire, TimeUnit.MINUTES);
                LogUtil.debug("[验证码存储] 成功，key: {}", key);
                return;
            } catch (Exception e) {
                if (i == maxRetries - 1) {
                    LogUtil.error("[验证码存储] 最终失败，key: {}", e, key);
                    throw new BlogException("验证码存储失败", e);
                }
                LogUtil.warn("[验证码存储] 第{}次重试，key: {}", i+1, key);
            }
        }
    }
}
